#include "../source/vlib.h"
#include <stdio.h>
#include <string.h>

static void test_find(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    iterator it;

    it = alg_find(it_begin(a2v(array)), it_end(a2v(array)), la(int, 2)); // find the first 2 in forward
    if (it_valid(it))
    {
        printf("find forward `2` at index: %d\r\n", it.pos);
    }

    it = alg_find(it_end(a2v(array)), it_begin(a2v(array)), la(int, 2)); // find the first 2 in reverse
    if (it_valid(it) == 1)
    {
        printf("find reverse `2` at index: %d\r\n", it.pos);
    }
}

static int find_if_pred(void *data)
{
    if (*(int *)data > 4) return 1;
    return 0;
}

static void test_find_if(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    iterator it;

    it = alg_find_if(it_begin(a2v(array)), it_end(a2v(array)), find_if_pred); // find the first number that larger than 4 in forward
    if (it_valid(it))
    {
        printf("find forward larger than `4` at index: %d, value = %d\r\n", it.pos, *(int *)it_value(it));
    }

    it = alg_find_if(it_end(a2v(array)), it_begin(a2v(array)), find_if_pred); // find the first number that larger than 4 in reverse
    if (it_valid(it) == 1)
    {
        printf("find reverse larger than `4` at index: %d, value = %d\r\n", it.pos, *(int *)it_value(it));
    }
}

static void test_count(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    int count;

    count = alg_count(it_begin(a2v(array)), it_end(a2v(array)), la(int, 2)); // count 2
    printf("count `2`: %d\r\n", count);

    count = alg_count(it_begin(a2v(array)), it_end(a2v(array)), la(int, 100)); // count 100
    printf("count `100`: %d\r\n", count);
}

static int count_if_pred(void *data)
{
    if (*(int *)data % 2 == 1) return 1;
    return 0;
}

static void test_count_if(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    int count;

    count = alg_count_if(it_begin(a2v(array)), it_end(a2v(array)), count_if_pred); // count odd number
    printf("count odd number: %d\r\n", count);
}

static void for_each_handle(iterator it)
{
    printf("it[%d] = %d\r\n", *(int *)it_key(it), *(int *)it_value(it));
}

static void test_for_each(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    list_t list = list(int);

    for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
    {
        list_push_back(list, &array[i]);
    }

    printf("--- for each array:\r\n");
    alg_for_each(it_begin(a2v(array)), it_end(a2v(array)), for_each_handle);

    printf("--- for each list:\r\n");
    alg_for_each(it_end(list), it_begin(list), for_each_handle);

    _list(list);
}

static void test_reverse(void)
{
    int array[5] = {2, 3, 5, 2, 7};

    alg_reverse(it_begin(a2v(array)), it_end(a2v(array)));

    for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static int rise_rule(void *front, void *back)
{
    if (*(int *)front < *(int *)back) return 1;
    else if (*(int *)front > *(int *)back) return -1;
    return 0;
}

static void test_sort(void)
{
    int array[5] = {2, 3, 5, 2, 7};

    alg_sort(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_bubble(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_select(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_insert(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_shell(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_quick(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // alg_sort_heap(it_begin(a2v(array)), it_end(a2v(array)), rise_rule);
    // printf("r = %d\r\n", r);
    for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static void test_fill(void)
{
    vector_t v = vector(int, 6);

    alg_fill(it_begin(v), it_end(v), la(int, 1024));

    for (int i = 0; i < vector_size(v); i++)
    {
        printf("v[%d] = %d\r\n", i, vector_at(v, int, i));
    }

    _vector(v);
}

static void test_fill_n(void)
{
    vector_t v = vector(int, 6);

    alg_fill_n(it(v, 2), 3, la(int, 1024));

    for (int i = 0; i < vector_size(v); i++)
    {
        printf("v[%d] = %d\r\n", i, vector_at(v, int, i));
    }

    _vector(v);
}

static void test_copy(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    vector_t v = vector(int, 6);

    alg_copy(it_begin(a2v(array)), it_end(a2v(array)), it_begin(v));

    for (int i = 0; i < vector_size(v); i++)
    {
        printf("v[%d] = %d\r\n", i, vector_at(v, int, i));
    }

    _vector(v);
}

static void test_unique(void)
{
    int array[10] = {2, 3, 3, 5, 7, 7, 7, 9, 16, 1024};
    int num = 0;

    num = alg_unique(it_begin(a2v(array)), it_end(a2v(array)), NULL);

    for (int i = 0; i < 10 - num; i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static void test_replace(void)
{
    int array[5] = {2, 3, 5, 2, 7};

    alg_replace(it_begin(a2v(array)), it_end(a2v(array)), la(int, 2), la(int, 1024)); // Replace 2 with 1024

    for (int i = 0; i < 5; i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static int replace_pred(void *data)
{
    if (*(int *)data > 4) return 1;
    return 0;
}

static void test_replace_if(void)
{
    int array[5] = {2, 3, 5, 2, 7};

    alg_replace_if(it_begin(a2v(array)), it_end(a2v(array)), replace_pred, la(int, 1024)); // Replace the number greater than 4 with 1024

    for (int i = 0; i < 5; i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static void test_remove(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    int num = 0;

    num = alg_remove(it_begin(a2v(array)), it_end(a2v(array)), la(int, 2)); // remove 2

    for (int i = 0; i < 5 - num; i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static int remove_pred(void *data)
{
    if (*(int *)data > 4) return 1;
    return 0;
}

static void test_remove_if(void)
{
    int array[5] = {2, 3, 5, 2, 7};
    int num = 0;

    num = alg_remove_if(it_begin(a2v(array)), it_end(a2v(array)), remove_pred); // remove numbers greater than 4

    for (int i = 0; i < 5 - num; i++)
    {
        printf("array[%d] = %d\r\n", i, array[i]);
    }
}

static void test_search(void)
{
    str_t s = str("Hello world!");
    char t[] = "llo";

    iterator it = alg_search(it_begin(s), it_end(s), it(a2v(t), 0), it(a2v(t), 2));
    if (it_valid(it))
    {
        printf("search pos: %d\r\n", it.pos);
    }
    else 
    {
        printf("search fail!\r\n");
    }

    _str(s);
}

void test_algorithm(void)
{
    // test_find();
    // test_find_if();
    // test_count();
    // test_count_if();
    // test_for_each();
    // test_reverse();
    // test_sort();
    // test_fill();
    // test_fill_n();
    // test_copy();
    // test_unique();
    // test_replace();
    // test_replace_if();
    // test_remove();
    // test_remove_if();
    test_search();
}

#if 0
void test_algorithm(void)
{
    vector_t vector = vector(int, 10);
    queue_t queue = queue(int, 10);
    iterator it;
    int i;
    char c;
    
    for (i = 0; i < vector_size(vector); i++)
    {
        vector_at(vector, int, i) = i;
    }

#if 0
    for (i = 0; i < 10; i++)
    {
        deque_push_back(dd, &i);
    }

    for (it = it_begin(dd); it.pos < it.object->size; it_move(it, 1))
    {
        printf("dd[%d] = %d\r\n", it.pos, *(int *)it_value(it));
    }
#endif 
    
    for (i = 0; i < queue_capacity(queue); i++)
    {
        queue_push(queue, &i);
    }
    
    for (it = it_begin(queue), it_move(it, 3); it.pos < it.object->size - 2; it_move(it, 1))
    {
        printf("queue[%d] = %d\r\n", it.pos, *(int *)it_value(it));
    }
    it = it_begin(queue);
    printf("move %d\r\n", it_move(it, 101));
    
    alg_sort(it_begin(queue), it_end(queue), 0, sort_rule_int);
    
    printf("------------------------\r\n");
    for (it = it_begin(queue); it.pos < it.object->size; it_move(it, 1))
    {
        printf("%d\r\n", *(int *)it_value(it));
       }
    
    str_t s = str("Hello world!");
    
    c = 'l';
    it = alg_find(it_begin(s), it_end(s), &c);
    while (it_valid(it))
    {
        printf("s[%d]\r\n", it.pos);
        it_move(it, 1);
        it = alg_find(it, it_end(s), &c);
    }
    
    // alg_sort(it(s, 2), it(s, str_length(s) - 2), 0, sort_rule_char);
    for (it = it(s, 0); it.pos < it.object->size; it_move(it, 1))
    {
        putchar(*(char *)it_value(it));
    }
    printf("\r\n");
    alg_reverse(it_begin(s), it_end(s));
    printf("s: %s\r\n", _S(s));
    
    _queue(queue);
    _vector(vector);
    
    printf("------------------------\r\n");
    printf("------------------------\r\n");
    // test_sort();
}
#endif
